/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package EasyInterface;

import DataAccess.StaticsWriter;
import Domain.State;
import Domain.Cluster;
import Domain.HmmStatic;
import Domain.Output;
import FMMLogic.*;
import be.ac.ulg.montefiore.run.jahmm.Hmm;
import be.ac.ulg.montefiore.run.jahmm.ObservationDiscrete;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.List;
/**
 *
 * @author Allen
 */
public class Main {

    /**
     * @param args the command line arguments
     * args[0] cmd;
     * args[1] command to execute;
     *  training
     *   - cmd -t [sourcefile] [resultfile]
     *
     *  predicting
     *      //output to screen and file
     *   - cmd -p [observation file&price file] [resultfile]
     *   - cmd -p [observation file&price file] //output to screen
     *
     *  training and predicting
     *   - cmd -h [sourcefile] [resultfile] []
     *
     *  shell mode
     *   - cmd s
     *
     */
    
    private static final int TRAINING = 1;
    private static final int PREDICTING = 2;
    private static final int HYBRID = 3;
    private static final int SHELL = 4;
    private static final int HELP = 5;
    private static ArrayList<HmmStatic> hmms;
    private static List<Hmm<ObservationDiscrete<Output>>> hmm_list;
    private static List<Cluster> clusters;

     public static Cluster getCluster(Hmm<ObservationDiscrete<Output>> curr_hmm)
    {
        Cluster tmpcluster =null;
        Cluster nearestcluster = null;
        double distance = 1000000;
        for (int i=0; i<clusters.size(); i++) {
            tmpcluster = clusters.get(i);
            if (tmpcluster.distance(curr_hmm)< distance){
                nearestcluster = tmpcluster;
                distance = tmpcluster.distance(curr_hmm);
            }
        }
        if (distance == 1000000)
            System.out.println("ERROR! NO cluster for current hmm");
        return nearestcluster;
    }
    public static void do_process(String input, String output ) {
        /*initial variables for single hmm*/
        int clusternumber = 3;
        Hmm<ObservationDiscrete<Output>> curr_hmm = null;
        HmmStatic curr_static_hmm = null;
        Output [] curr_output = null;
        Cluster curr_cluster = null;
        State [] curr_state =null;


        /*initial variables for hmm lists*/
        List<Output[]> output_list = new ArrayList<Output[]>();

        /*train the hmms*/
        LFMMTrainer fmm = new LFMMTrainer(input);
        fmm.train(output);
        hmm_list = fmm.getHmms();
        output_list = fmm.getOutputs();
        hmms = new ArrayList<HmmStatic>();

        /*cluster the models*/
        LFMMCluster lc = new LFMMCluster(clusternumber);
        clusters = lc.kmeansCalculate(hmm_list);

        /*do prediction on the models */
        LFMMPredictor pdc = new LFMMPredictor();

        for(int i = 0; i < hmm_list.size(); i ++){
            //setting hmm
            curr_hmm = hmm_list.get(i);
            curr_static_hmm = new HmmStatic();
            curr_static_hmm.setHmm(curr_hmm);
            //setting output
            curr_output = output_list.get(i);
            curr_static_hmm.setOutput(curr_output);
            //setting cluster
            curr_cluster = getCluster(curr_hmm);
            curr_static_hmm.setCluster(curr_cluster);
            //setting States
            pdc.initViterbi(curr_output, curr_hmm);
            curr_state = pdc.stateSequence();
            curr_static_hmm.setStates(curr_state);
            hmms.add(curr_static_hmm);
        }
    }
    
    public static HmmStatic do_singlePredict(String input, String output)
    {
        LFMMTrainer fmm = new LFMMTrainer(input);
        fmm.train("data_test/result.txt");
        List<Hmm<ObservationDiscrete<Output>>> loc_hmmlist = fmm.getHmms();
        List<Output[]> loc_outputlist = fmm.getOutputs();
        Hmm<ObservationDiscrete<Output>> currHmm = loc_hmmlist.get(0);
        Output[] currOutput = loc_outputlist.get(0);
        State[] predict_State = null;
        Cluster curr_cluster = null;
        Cluster predict_cluster = null;
        //do viterbi prediction
        LFMMPredictor pdc = new LFMMPredictor();
        pdc.initViterbi(currOutput, currHmm);
        predict_State = pdc.stateSequence();

        //do fb predict
        double[] probArray = new double[clusters.size()];
        for (int j=0; j < clusters.size(); j++) {
            curr_cluster = clusters.get(j);
            Hmm<ObservationDiscrete<Output>> testhmm =
                                                   curr_cluster.getAvgHmm();
            pdc.initFb(currOutput, testhmm);
            probArray[j] = pdc.Probability();
            }
        double smallest =probArray[0];
        int index =0;
        for ( int i=1; i<clusters.size(); i++) {
            if (probArray[i]< smallest) {
                index = i;
                smallest = probArray[i];
            }
        }
        predict_cluster = clusters.get(index);

        HmmStatic resulthmm = new HmmStatic();
        resulthmm.setCluster(predict_cluster);
        resulthmm.setHmm(currHmm);
        resulthmm.setOutput(currOutput);
        resulthmm.setStates(predict_State);

        return resulthmm;
    }

    public static void main(String[] args) throws IOException {

        int argn = args.length;
        int mode = 0;
        File test_file=null;

        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        String trainingfile = null;
        boolean flag =false;
        while (!flag){
            flag =true;
            System.out.println("Type in \"exit\" to exit any time");
            System.out.print("Indicate training file:");
            trainingfile = br.readLine();
            if(trainingfile.compareTo("exit") == 0){
                System.out.println("exiting");
                return;
            }
            test_file= new File(trainingfile);
            if (!test_file.exists()) {
                System.out.println("File: "+trainingfile+" not exist!");
                flag= false;
                continue;
            }
        }
        System.out.println("Start Training...");
        do_process(trainingfile,"data_test/hmm_results.txt");
        System.out.println("Finish Training.");
/*output the training set result*/
        System.out.println("Stream HMM result to(type in \"n\" to skip this step):");
        String input2 = br.readLine();
        flag = false;
        if(input2.compareTo("n") == 0)
            flag = false;
        else
            flag = true;
        if(input2.compareTo("exit") == 0){
            System.out.println("exiting");
            return;
        }
        if(flag) {
            try{
                FileOutputStream fs = new FileOutputStream(input2);
                PrintStream ps = new PrintStream(fs);
                StaticsWriter sw = new StaticsWriter();
                sw.setStatics(hmms);
                sw.writeHmmStatics(ps);

                ps.close();
                fs.close();
            } catch(Exception e){
                e.printStackTrace();
            }
        }

        /*read the fund source for anaylyzing*/
        while(true){
            System.out.print("Indicate fund source to analyse:");
            String input = br.readLine();

            test_file = new File(input);
            if(input.compareTo("exit") == 0){
                System.out.println("Exiting");
                return;
            }

            if (!test_file.exists()) {
                System.out.println("File: "+input+" not exist!");
                continue;
            }
            HmmStatic hs = do_singlePredict(input, "test");
            StaticsWriter.writeSingleStatic(System.out, hs);
        }
    }
}
